/*
 *
 * only support for "exact division"
 *
 *
 *
 */


#include "aceMesh_runtime_c.h"
#include "slave.h"
#include "float.h"
#include "sbmd.h"
#include <stdlib.h>
#include <stdio.h>
#include "slave.h"
#include "3d7p_aceMesh.h"
#include "task.h"
#include "ldm_malloc.h"
__thread_local volatile unsigned long get_reply,put_reply;
//kernel region
__thread_local_fix double slave[2][BLKX][BLKY][BLKZ];
//Record the logical information of the last time executed task.
extern double A[2][SIZEX][SIZEY][SIZEZ];

__thread_local_fix int amt_flag = 0;


#define alpha_d 0.0876
#define beta_d  0.0765

void stencil_core(targs* args)
{  
   //0. assumptions       
   //data region: [starti-1,endi] [startj-1,endj] [startk-1,endk]  
   //compute region: [starti, endi) [startj,endj) [startk,endk)
  int dest,src,i,j,k,s_iter,s_starti,s_endi,s_startj,s_endj,s_startk,s_endk;
  int len, bsize, stride;   
  int i_region,j_region,k_region;
  int i_i,j_j,k_k;
  int tileno[3];
  int varno[2];
  int reuse[2];
  int lhs_no,rhs_no;
  int dma_num=0;
  s_iter = args->iter;
  dest = (s_iter) % 2 ;
  src = 1 - dest;
  s_starti = args->starti;
  s_endi = args->endi;
  s_startj = args->startj;
  s_endj = args->endj;
  s_startk = 1;
  s_endk = args->k_N-1;
  i_region = s_endi-s_starti-1;
  j_region = s_endj-s_startj-1;
  k_region = s_endk-s_startk-1;   
  i_i = s_endi-s_starti-2;
  j_j = s_endj-s_startj-2;
  k_k = s_endk-s_startk-2;
  if(amt_flag == 0)  
  {
    ldm_set_datatiles(2);//total ldm array
    amt_flag = 1;
  }
  tileno[0]=s_starti/BLKX;
  tileno[1]=s_startj/BLKY;
  tileno[2]=s_startk/BLKZ;
  slave_ldm_postreq_tile(s_iter, &A[dest][0][0][0], tileno, OUT);//
  slave_ldm_postreq_tile(s_iter, &A[src][0][0][0], tileno, IN);
  slave_ldm_getrep(2,varno,reuse);
  lhs_no = varno[0];
  rhs_no = varno[1];
  //1 update core data
  get_reply=0;
  if(rhs_no <0)
    printf("Array assignment failed\n");
  else if(reuse[1]==0){
   // printf("fetch core data\n");
    for(i = s_starti; i < s_endi; i++)
    {
       bsize=(BLKZ)*8;
       stride=(SIZEZ-(BLKZ))*8;  //stride: from blocktail to blockhead 
       len= bsize*(BLKY);
       athread_get(PE_MODE,&A[src][i][s_startj][s_startk]
                 , &slave[rhs_no][i-s_starti][0][0]
                 , len, &get_reply
                 , 0, stride, bsize);
    }
    dma_num+=(s_endi-s_starti);
  }
  //2.wait for core data finished 
  while(get_reply!=(dma_num));
  //3.compute core data
  for(i = 1; i < i_region; i++)
     for(j = 1; j < j_region; j++)
        for(k = 1; k < k_region; k++)
            slave[lhs_no][i][j][k] = alpha_d * slave[rhs_no][i][j][k] + \
                     beta_d * (slave[rhs_no][i-1][j][k] + slave[rhs_no][i+1][j][k] +\
                     slave[rhs_no][i][j-1][k] + slave[rhs_no][i][j+1][k]+\
                     slave[rhs_no][i][j][k-1] + slave[rhs_no][i][j][k+1]);
  //4.1 compute up and down line of k dimensional change
  for(k = 1; k < k_region; ++k)
  {
    //left face
//    if(k==1)
  //    printf("slave:%20.20f,%20.20f,%20.20f,%20.20f,%20.20f,%20.20f,%20.20f\n",
    //         slave[rhs_no][i_region][0][k],slave[rhs_no][i_i][0][k+s_startk],A[src][s_endi][s_startj][k+s_startk],
      //       A[src][s_endi-1][s_startj-1][k+s_startk],slave[rhs_no][i_region][1][k],slave[rhs_no][i_region][0][k-1],
        //     slave[rhs_no][i_region][0][k+1]);
    slave[lhs_no][0][0][k] = alpha_d * slave[rhs_no][0][0][k] + \
                     beta_d * (A[src][s_starti-1][s_startj][k+s_startk] + slave[rhs_no][1][0][k] +\
                     A[src][s_starti][s_startj-1][k+s_startk] + slave[rhs_no][0][1][k]+\
                     slave[rhs_no][0][0][k-1] + slave[rhs_no][0][0][k+1]);
    slave[lhs_no][i_region][0][k] = alpha_d * slave[rhs_no][i_region][0][k]+ \
                     beta_d * (slave[rhs_no][i_i][0][k] + A[src][s_endi][s_startj][k+s_startk] +\
                     A[src][s_endi-1][s_startj-1][k+s_startk] + slave[rhs_no][i_region][1][k]+\
                     slave[rhs_no][i_region][0][k-1]+ slave[rhs_no][i_region][0][k+1]);
   //right face 
    slave[lhs_no][0][j_region][k] = alpha_d * slave[rhs_no][0][j_region][k]+ \
                     beta_d * (A[src][s_starti-1][s_endj-1][k+s_startk] + slave[rhs_no][1][j_region][k] +\
                     slave[rhs_no][0][j_j][k] + A[src][s_starti][s_endj][k+s_startk]+\
                     slave[rhs_no][0][j_region][k-1] + slave[rhs_no][0][j_region][k+1]);
    slave[lhs_no][i_region][j_region][k] = alpha_d * slave[rhs_no][i_region][j_region][k] + \
                     beta_d * (slave[rhs_no][i_i][j_region][k] + A[src][s_endi][s_endj-1][k+s_startk] +\
                     slave[rhs_no][i_region][j_j][k] + A[src][s_endi-1][s_endj][k+s_startk]+\
                     slave[rhs_no][i_region][j_region][k-1] + slave[rhs_no][i_region][j_region][k+1]);
  }
  //4.2 compute left and right face 
   for(i = 1; i < i_region; ++i)
    for(k = 1; k < k_region; ++k)
    {
//      if(i==5&&k==1)
  //     printf("slave:%20.20f,%20.20f,%20.20f,%20.20f,%20.20f,%20.20f,%20.20f\n",
    //           slave[rhs_no][i][0][k],slave[rhs_no][i-1][0][k],slave[rhs_no][i+1][0][k],
      //         A[src][i+s_starti][s_startj-1][k+s_startk],slave[rhs_no][i][1][k],
        //       slave[rhs_no][i][0][k-1],slave[rhs_no][i][0][k+1]);
       slave[lhs_no][i][0][k] = alpha_d * slave[rhs_no][i][0][k] + \
                     beta_d * (slave[rhs_no][i-1][0][k] + slave[rhs_no][i+1][0][k] +\
                     A[src][i+s_starti][s_startj-1][k+s_startk] + slave[rhs_no][i][1][k]+\
                     slave[rhs_no][i][0][k-1] + slave[rhs_no][i][0][k+1]);
       slave[lhs_no][i][j_region][k] = alpha_d * slave[rhs_no][i][j_region][k] + \
                     beta_d * (slave[rhs_no][i-1][j_region][k] + slave[rhs_no][i+1][j_region][k] +\
                     slave[rhs_no][i][j_j][k] + A[src][i+s_starti][s_endj][k+s_startk]+\
                     slave[rhs_no][i][j_region][k-1] + slave[rhs_no][i][j_region][k+1]);
    }
   // 4.3 compute up and down face
    for(j = 1; j < j_region; ++j)
    for(k = 1; k < k_region; ++k)
    {
      slave[lhs_no][0][j][k] = alpha_d * slave[rhs_no][0][j][k]+ \
                     beta_d * (A[src][s_starti-1][j+s_startj][k+s_startk] + slave[rhs_no][1][j][k] +\
                     slave[rhs_no][0][j-1][k] + slave[rhs_no][0][j+1][k]+\
                     slave[rhs_no][0][j][k-1] + slave[rhs_no][0][j][k+1]);
      slave[lhs_no][i_region][j][k] =  alpha_d * slave[rhs_no][i_region][j][k] + \
                     beta_d * (slave[rhs_no][i_i][j][k] + A[src][s_endi][j+s_startj][k+s_startk] +\
                     slave[rhs_no][i_region][j-1][k] + slave[rhs_no][i_region][j+1][k]+\
                     slave[rhs_no][i_region][j][k-1] + slave[rhs_no][i_region][j][k+1]);
   }
  //5 compute 8 corner point
  //5.1 compute up_left_front_point
  slave[lhs_no][0][0][0] = alpha_d * slave[rhs_no][0][0][0] + \
                   beta_d * (A[src][s_starti-1][s_startj][s_startk] + slave[rhs_no][1][0][0] +\
                   A[src][s_starti][s_startj-1][s_startk] + slave[rhs_no][0][1][0]+\
                   A[src][s_starti][s_startj][s_startk-1] + slave[rhs_no][0][0][1]); 
  //5.2 compute up_left_back_point
  slave[lhs_no][0][0][k_region] = alpha_d * slave[rhs_no][0][0][k_region]+ \
                   beta_d * (A[src][s_starti-1][s_startj][s_endk-1] +\
                   slave[rhs_no][1][0][k_region] +\
                   A[src][s_starti][s_startj-1][s_endk-1] + slave[rhs_no][0][1][k_region]+\
                   slave[rhs_no][0][0][k_k] + A[src][s_starti][s_startj][s_endk]);
  
  //5.3 compute up_right_front_point
  slave[lhs_no][0][j_region][0] = alpha_d * slave[rhs_no][0][j_region][0]+ \
                     beta_d * (A[src][s_starti-1][s_endj-1][s_startk] +\
                     slave[rhs_no][1][j_region][0] +\
                     slave[rhs_no][0][j_j][0] + A[src][s_starti][s_endj][s_startk]+\
                     A[src][s_starti][s_endj-1][s_startk-1] + slave[rhs_no][0][j_region][1]);
  
  //5.4 compute up_right_back_point
  slave[lhs_no][0][j_region][k_region] = alpha_d * \
                    slave[rhs_no][0][j_region][k_region]+ \
                     beta_d * (A[src][s_starti-1][s_endj-1][s_endk-1] + \
                     slave[rhs_no][1][j_region][k_region] + \
                     slave[rhs_no][0][j_j][k_region] + \
                     A[src][s_starti][s_endj][s_endk-1]+\
                     slave[rhs_no][0][j_region][k_k] + \
                     A[src][s_starti][s_endj-1][s_endk]);  
  //5.5 compute down_left_front_point
   slave[lhs_no][i_region][0][0] =   alpha_d * slave[rhs_no][i_region][0][0] + \
                     beta_d * (slave[rhs_no][i_i][0][0] + A[src][s_endi][s_startj][s_startk] +\
                     A[src][s_endi-1][s_startj-1][s_startk] + slave[rhs_no][i_region][1][0]+\
                     A[src][s_endi-1][s_startj][s_startk-1] + slave[rhs_no][i_region][0][1]);  
  //5.6 compute down_left_back_point
   slave[lhs_no][i_region][0][k_region] = alpha_d * \
                     slave[rhs_no][i_region][0][k_region] + \
                     beta_d * (slave[rhs_no][i_i][0][k_region] + \
                     A[src][s_endi][s_startj][s_endk-1] +\
                     A[src][s_endi-1][s_startj-1][s_endk-1] +\
                     slave[rhs_no][i_region][1][k_region]+\
                     slave[rhs_no][i_region][0][k_k] +\
                     A[src][s_endi-1][s_startj][s_endk]);    
  //5.7 compute down_right_front_point
  slave[lhs_no][i_region][j_region][0] = alpha_d * \
                     slave[rhs_no][i_region][j_region][0] + \
                     beta_d * (slave[rhs_no][i_i][j_region][0] + \
                     A[src][s_endi][s_endj-1][s_startk] + \
                     slave[rhs_no][i_region][j_j][0] + \
                     A[src][s_endi-1][s_endj][s_startk]+ \
                     A[src][s_endi-1][s_endj-1][s_startk-1] + \
                     slave[rhs_no][i_region][j_region][1]); 
  //5.8  compute down_right_back_point
  slave[lhs_no][i_region][j_region][k_region] = alpha_d * \
                     slave[rhs_no][i_region][j_region][k_region] + \
                     beta_d * (slave[rhs_no][i_i][j_region][k_region] + \
                     A[src][s_endi][s_endj-1][s_endk-1] + \
                     slave[rhs_no][i_region][j_j][k_region] + \
                     A[src][s_endi-1][s_endj][s_endk-1]+ \
                     slave[rhs_no][i_region][j_region][k_k] + \
                     A[src][s_endi-1][s_endj-1][s_endk]); 
 //6 compute 12 line data
  //6.1 compute up and down line of j dimensional  change 
  for(j = 1;j < j_region; ++j)
  {
  //front face
    slave[lhs_no][0][j][0] = alpha_d * slave[rhs_no][0][j][0] + \
                     beta_d * (A[src][s_starti-1][j+s_startj][s_startk] + slave[rhs_no][1][j][0] +\
                     slave[rhs_no][0][j-1][0] + slave[rhs_no][0][j+1][0]+\
                     A[src][s_starti][j+s_startj][s_startk-1] + slave[rhs_no][0][j][1]);
    slave[lhs_no][i_region][j][0] = alpha_d * slave[rhs_no][i_region][j][0] + \
                     beta_d * (slave[rhs_no][i_i][j][0] + A[src][s_endi][j+s_startj][s_startk] +\
                     slave[rhs_no][i_region][j-1][0] + slave[rhs_no][i_region][j+1][0]+\
                     A[src][s_endi-1][j+s_startj][s_startk-1] + slave[rhs_no][i_region][j][1]);
  //back face
    slave[lhs_no][0][j][k_region] = alpha_d * slave[rhs_no][0][j][k_region] + \
                     beta_d * (A[src][s_starti-1][j+s_startj][s_endk-1] + slave[rhs_no][1][j][k_region] +\
                     slave[rhs_no][0][j-1][k_region] + slave[rhs_no][0][j+1][k_region]+\
                     slave[rhs_no][0][j][k_k] + A[src][s_starti][j+s_startj][s_endk]);
    slave[lhs_no][i_region][j][k_region] =  alpha_d * slave[rhs_no][i_region][j][k_region] + \
                     beta_d * (slave[rhs_no][i_i][j][k_region] + A[src][s_endi][j+s_startj][s_endk-1] +\
                     slave[rhs_no][i_region][j-1][k_region] + slave[rhs_no][i_region][j+1][k_region]+\
                     slave[rhs_no][i_region][j][k_k] + A[src][s_endi-1][j+s_startj][s_endk]);
  }
  //6.2 compute left and right line of i dimensional change
  for(i = 1;i < i_region; ++i)
  {
   //front face
    slave[lhs_no][i][0][0] = alpha_d * slave[rhs_no][i][0][0] + \
                     beta_d * (slave[rhs_no][i-1][0][0] + slave[rhs_no][i+1][0][0] +\
                     A[src][i+s_starti][s_startj-1][s_startk] + slave[rhs_no][i][1][0]+\
                     A[src][i+s_starti][s_startj][s_startk-1] + slave[rhs_no][i][0][1]);
    slave[lhs_no][i][j_region][0] = alpha_d * (slave[rhs_no][i][j_region][0]) + \
                     beta_d * (slave[rhs_no][i-1][j_region][0] + slave[rhs_no][i+1][j_region][0] +\
                     slave[rhs_no][i][j_j][0] + A[src][i+s_starti][s_endj][s_startk]+\
                     A[src][i+s_starti][s_endj-1][s_startk-1] + slave[rhs_no][i][j_region][1]); 
  //back face 
    slave[lhs_no][i][0][k_region] = alpha_d * slave[rhs_no][i][0][k_region] + \
                     beta_d * (slave[rhs_no][i-1][0][k_region] + slave[rhs_no][i+1][0][k_region] +\
                     A[src][i+s_starti][s_startj-1][s_endk-1] + slave[rhs_no][i][1][k_region]+\
                     slave[rhs_no][i][0][k_k] + A[src][i+s_starti][s_startj][s_endk]); 
    slave[lhs_no][i][j_region][k_region] = alpha_d * slave[rhs_no][i][j_region][k_region] + \
                     beta_d * (slave[rhs_no][i-1][j_region][k_region] + slave[rhs_no][i+1][j_region][k_region] +\
                     slave[rhs_no][i][j_j][k_region] + A[src][i+s_starti][s_endj][s_endk-1]+\
                     slave[rhs_no][i][j_region][k_k] + A[src][i+s_starti][s_endj-1][s_endk]);
  }
  // compute 2 face
  //7.1 compute front and back face 
  for(i = 1; i < i_region; ++i)
    for(j = 1; j < j_region; ++j)
    {
       slave[lhs_no][i][j][0] = alpha_d * slave[rhs_no][i][j][0] + \
                     beta_d * (slave[rhs_no][i-1][j][0] + slave[rhs_no][i+1][j][0] +\
                     slave[rhs_no][i][j-1][0] + slave[rhs_no][i][j+1][0]+\
                     A[src][i+s_starti][j+s_startj][s_startk-1] + slave[rhs_no][i][j][1]); 
       slave[lhs_no][i][j][k_region] = alpha_d * slave[rhs_no][i][j][k_region] + \
                     beta_d * (slave[rhs_no][i-1][j][k_region] + slave[rhs_no][i+1][j][k_region] +\
                     slave[rhs_no][i][j-1][k_region] + slave[rhs_no][i][j+1][k_region]+\
                     slave[rhs_no][i][j][k_k] + A[src][i+s_starti][j+s_startj][s_endk]);  
    }
  //8 write back
   put_reply=0;
   for(i =  s_starti; i < s_endi; ++i)
   {   
     int len,bsize,stride;
     stride = (SIZEZ-(BLKZ))*8;
     bsize = (BLKZ)*8;
     len = bsize*(BLKY);
     athread_put(PE_MODE,&slave[lhs_no][i-s_starti][0][0]
                     , &A[dest][i][s_startj][s_startk]
                     ,len,&put_reply,stride,bsize);
   }   
   while(put_reply!=(BLKX)); 
}
